package io.hellsinger.filesystem.linux.operation

import io.hellsinger.filesystem.linux.LinuxFileOperationProvider
import io.hellsinger.filesystem.linux.LinuxFileOperationProvider.getByte
import io.hellsinger.filesystem.linux.LinuxOperationOptions.Open
import io.hellsinger.filesystem.linux.file.LinuxFile
import io.hellsinger.filesystem.path.Path
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

/**
 * Reads given [LinuxFile]
 * Uses [bufferPointer] to fill itself with [readChunkSize] chunk
 * **/
fun <P : Path> LinuxFile<P>.readIntoBuffer(
    flags: Int = Open.NON_BLOCKING,
    mode: Int = DEFAULT_OPEN_MODE,
    bufferPointer: Long,
    readChunkSize: Int = DEFAULT_BUFFER_SIZE,
): Flow<Byte> =
    flow {
        val fd =
            LinuxFileOperationProvider.openFileDescriptor(
                path.bytes,
                flags or Open.READ_ONLY,
                mode,
            )
        var position = 0
        while (true) {
            val count = LinuxFileOperationProvider.read(fd, position, bufferPointer, readChunkSize)
            if (count <= 0) break // Reached end of file or error (EOF)
            for (i in 0..count) emit(getByte(bufferPointer = bufferPointer, index = position + i))
            position += count
        }
        LinuxFileOperationProvider.closeFileDescriptor(fd)
    }

/**
 * Reads given [LinuxFile]
 * Uses [bufferSize] to allocate buffer and [readChunkSize] to fill it with chunk
 * Automatically clean buffer after emitting all bytes
 * **/
fun <P : Path> LinuxFile<P>.read(
    flags: Int = Open.NON_BLOCKING,
    mode: Int = DEFAULT_OPEN_MODE,
    bufferSize: Long = DEFAULT_BUFFER_SIZE.toLong(),
    readChunkSize: Int = DEFAULT_BUFFER_SIZE,
): Flow<Byte> =
    flow {
        val buffer = LinuxFileOperationProvider.allocate(bufferSize)
        readIntoBuffer(flags, mode, buffer, readChunkSize).collect(::emit)
        LinuxFileOperationProvider.free(buffer)
    }

/**
 * Transforms a flow of byte to separated lines of file
 * **/
fun Flow<Byte>.lines(): Flow<ByteArray> =
    flow {
        val buffer = mutableListOf<Byte>()
        collect { byte ->
            if (byte == '\n'.code.toByte()) {
                emit(buffer.toByteArray())
                buffer.clear()
            } else {
                buffer.add(byte)
            }
        }
    }
